/*
    Copyright (C) 2020-2021 Vincent Buso <vincent.buso@funkey-project.com>
    Copyright (C) 2021 Michel Stempin <michel.stempin@funkey-project.com>

    This file is part of the FunKey S GPIO keyboard daemon.

    This is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    The software is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with the GNU C Library; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  
*/

/**
 *  @file termfix.c
 *  This file contains a function to unlock and force a vt back into text mode
 */
#if 0
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <linux/vt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/kd.h>
#include <linux/keyboard.h>

int main(int argc, char** argv)
{
    if (argc != 2) {
        printf("usage: termfix /dev/ttyX\n");
        return 2;
    }

    int fd = open(argv[1], O_RDWR, 0);
    int res = ioctl(fd, VT_UNLOCKSWITCH, 1);

    if (res != 0) {
        perror("ioctl VT_UNLOCKSWITCH failed");
        return 3;
    }

    ioctl(fd, KDSETMODE, KD_TEXT);

    if (res != 0) {
        perror("ioctl KDSETMODE failed");
        return 3;
    }

    printf("Success\n");

    return res;
}
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <getopt.h>
#include <unistd.h>
#include "daemon.h"
#include "uinput.h"
#include "gpio_mapping.h"
#include "gpio_axp209.h"
#include <signal.h>
#include <fcntl.h>  
#include <sys/mman.h>  
#include <sys/ioctl.h>  
#include <linux/fb.h>  

#define DEBUG_MAIN 

#ifdef DEBUG_MAIN
#define FK_DEBUG(...) printf(__VA_ARGS__)
#else
#define FK_DEBUG(...)
#endif

#ifdef ERROR_MAIN
#define FK_ERROR(...) printf(__VA_ARGS__)
#else
#define FK_ERROR(...)
#endif
#define SHELL_COMMAND_SHUTDOWN "powerdown schedule 0.1"

#pragma pack(push, 1)  
typedef struct {  
    unsigned short bfType;  
    unsigned int bfSize;  
    unsigned short bfReserved1;  
    unsigned short bfReserved2;  
    unsigned int bfOffBits;  
} BITMAPFILEHEADER;  
  
typedef struct {  
    unsigned int biSize;  
    int biWidth;  
    int biHeight;  
    unsigned short biPlanes;  
    unsigned short biBitCount;  
    unsigned int biCompression;  
    unsigned int biSizeImage;  
    int biXPelsPerMeter;  
    int biYPelsPerMeter;  
    unsigned int biClrUsed;  
    unsigned int biClrImportant;  
} BITMAPINFOHEADER;  
#pragma pack(pop)  

const char *bmp_file = "/mnt/FunKey/Image/poweroff.bmp";
const char *fb_device = "/dev/fb0";  

// 16-bit RGB565 color conversion  
unsigned short rgb24_to_rgb565(unsigned char r, unsigned char g, unsigned char b) {  
    return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);  
}

int show_bmp24_on_16bit(void)
{
   int fb_fd = open(fb_device, O_RDWR);  
    if (fb_fd == -1) {  
        perror("Error: cannot open framebuffer device");  
        return 1;  
    }  
  
    struct fb_var_screeninfo vinfo;  
    if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo)) {  
        perror("Error reading variable information");  
        close(fb_fd);  
        return 1;  
    }  
  
    // Ensure the framebuffer resolution and depth match our expectations  
    if (vinfo.xres != 800 || vinfo.yres != 480 || vinfo.bits_per_pixel != 16) {  
        fprintf(stderr, "Error: framebuffer resolution or depth does not match expectations\n");  
        close(fb_fd);  
        return 1;  
    }  
  
    FILE *bmp = fopen(bmp_file, "rb");  
    if (!bmp) {  
        perror("Error opening BMP file");  
        close(fb_fd);  
        return 1;  
    }  
  
    BITMAPFILEHEADER file_header;  
    BITMAPINFOHEADER info_header;  
    fread(&file_header, sizeof(BITMAPFILEHEADER), 1, bmp);  
    fread(&info_header, sizeof(BITMAPINFOHEADER), 1, bmp);  
  
    if (file_header.bfType != 0x4D42 || info_header.biBitCount != 24 || info_header.biCompression != 0) {  
        fprintf(stderr, "Error: Unsupported BMP format\n");  
        fclose(bmp);  
        close(fb_fd);  
        return 1;  
    }  
  
    int width = info_header.biWidth;  
    int height = info_header.biHeight;  
    int stride = (width * 3 + 3) & ~3; // Ensure stride is a multiple of 4  
  
    unsigned char *bmp_data = (unsigned char *)malloc(stride * height);  
    fseek(bmp, file_header.bfOffBits, SEEK_SET);  
    fread(bmp_data, 1, stride * height, bmp);  
    fclose(bmp);  
  
    long screensize = vinfo.yres_virtual * vinfo.xres_virtual * vinfo.bits_per_pixel / 8;  
    unsigned short *fbp = (unsigned short *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);  
    if ((int)fbp == -1) {  
        perror("Error: failed to map framebuffer device to memory");  
        close(fb_fd);  
        free(bmp_data);  
        return 1;  
    }  
  
    // Center the BMP image on the framebuffer  
    int x_offset = (800 - width) / 2;  
    int y_offset = (480 - height) / 2;  
  
    for (int y = 0; y < height; y++) {  
        for (int x = 0; x < width; x++) {  
            int fb_x = x_offset + x;  
            int fb_y = y_offset + y;  
            int fb_index = (fb_y * 800 + fb_x);  
  
            unsigned char *pixel = &bmp_data[(height - 1 - y) * stride + x * 3];  
            fbp[fb_index] = rgb24_to_rgb565(pixel[2], pixel[1], pixel[0]);  
        }  
    }  
    FK_DEBUG("show bmp ok!\n");
    munmap(fbp, screensize);  
    close(fb_fd);  
    free(bmp_data);  
	return 0;
}

int main(int argc, char *argv[])
{
    int val_int_bank_4;
    if (true == axp209_init())
    {
        FK_DEBUG("axp209_init\n");
    }
    else
    {
        FK_ERROR("axp209_init fail\n");
        return EXIT_FAILURE;
    }
    // 注册信号处理程序
    // signal(SIGTERM, poweroff_handler);
    while (true)
    {
        val_int_bank_4 = axp209_read_interrupt_bank_4();
        if (val_int_bank_4 < 0 || val_int_bank_4 == 0xFF)
        {
            FK_DEBUG("axp209_read_interrupt_bank_4 fail\n");
            return EXIT_FAILURE;
        }
        else if (val_int_bank_4 & AXP209_INTERRUPT_PEK_N_OE_POWEROFF)
        {
            FK_DEBUG("return_ON poweroff\n");
            //show_bmp24_on_16bit();
            system(SHELL_COMMAND_SHUTDOWN);
            show_bmp24_on_16bit();
        }

        FK_DEBUG("N_OE = %d\n", val_int_bank_4);
        usleep(200 * 1000); // 200ms
    }
    /* Deinitialize the AXP209 PMIC chip */
    if (true == axp209_deinit())
    {
        FK_DEBUG("axp209_deinit\n");
    }
    else
    {
        FK_ERROR("axp209_deinit fail\n");
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
